Skip to content

Commit

Permalink
Merge pull request #25 from sandeep-vedam/feature/create-app-2
Browse files Browse the repository at this point in the history
Feature/create app 2
  • Loading branch information
michielvandergeest authored Feb 2, 2024
2 parents a9545d0 + fa5e917 commit 6780894
Show file tree
Hide file tree
Showing 32 changed files with 3,881 additions and 174 deletions.
220 changes: 220 additions & 0 deletions bin/helpers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
import { green, bold, red } from 'kolorist'
import path from 'path'
import { execa } from 'execa'
import fs from 'fs'
import replaceInFile from 'replace-in-file'
import ora from 'ora'
const spinner = ora()

/**
* This function copies Lightning fixtures to the target directory for creating a L3 application.
*/

export const copyLightningFixtures = (config) => {
return new Promise((resolve) => {
const targetDir = config.appFolder || ''
if (config.appFolder && fs.existsSync(targetDir)) {
exit(red(bold('The target directory ' + targetDir + ' already exists')))
}
//this will be removed once ts support is added
fs.cpSync(path.join(path.join(config.fixturesBase, 'js'), 'default'), targetDir, {
recursive: true,
})
fs.cpSync(path.join(config.fixturesBase, 'common/public'), path.join(targetDir, 'public'), {
recursive: true,
})

resolve(targetDir)
})
}

/**
* This function adds eslint related configuration to the project folder to be created
* @param config
* @returns {boolean}
*/
export const addESlint = (config) => {
// Make husky dir
fs.mkdirSync(path.join(config.targetDir, '.husky'), { recursive: true })

// Copy husky hook
fs.copyFileSync(
path.join(config.fixturesBase, 'common/eslint/husky/pre-commit'),
path.join(config.targetDir, '.husky/pre-commit')
)

// Copy editor config from common
fs.copyFileSync(
path.join(config.fixturesBase, 'common/eslint/.editorconfig'),
path.join(config.targetDir, '.editorconfig')
)

// Copy eslintignore from common
fs.copyFileSync(
path.join(config.fixturesBase, 'common/eslint/.eslintignore'),
path.join(config.targetDir, '.eslintignore')
)

// Copy eslintrc.js from fixtured specfic directory
fs.copyFileSync(
path.join(config.fixturesBase, 'common/eslint/.eslintrc.cjs'),
path.join(config.targetDir, '.eslintrc.cjs')
)

// Copy IDE stuff from fixture base
fs.cpSync(path.join(config.fixturesBase, 'common/ide'), path.join(config.targetDir), {
recursive: true,
})

// Copy and merge fixture specific package.json
const origPackageJson = JSON.parse(fs.readFileSync(path.join(config.targetDir, 'package.json')))
const eslintPackageJson = JSON.parse(
fs.readFileSync(path.join(config.fixturesBase, 'common/eslint/package.json'))
)
fs.writeFileSync(
path.join(config.targetDir, 'package.json'),
JSON.stringify(
{
...origPackageJson,
...eslintPackageJson,
devDependencies: {
...(origPackageJson.devDependencies || {}),
...(eslintPackageJson.devDependencies || {}),
},
},
null,
2
)
)

return true
}

/**
* This function sets the version by fetching the latest blits version
* @param config
* @returns {Promise<unknown>}
*/
export const setBlitsVersion = (config) => {
return new Promise((resolve, reject) => {
execa('npm', ['view', '@lightningjs/blits', 'version'])
.then(({ stdout }) => {
replaceInFile.sync({
files: config.targetDir + '/*',
from: /\{\$blitsVersion\}/g,
to: '^' + stdout,
})
resolve()
})
.catch((e) => {
spinnerMsg.fail(`Error occurred while setting blits version\n\n${e}`)
reject()
})
})
}

/**
* Display a message and start a spinner with the specified message.
*
* @param {string} msg - The message to display while starting the spinner.
*/
export const spinnerMsg = {
start(msg) {
console.log(' ')
spinner.start(msg)
},
stop() {
spinner.stop()
},
succeed(msg) {
spinner.succeed(msg)
},
fail(msg) {
spinner.fail(msg)
console.log(' ')
},
warn(msg) {
spinner.warn(msg)
},
}

/**
* This function replaces placeholders like '{$appName}' and '{$appPackage}' in files within the target directory
* with the actual values from the configuration object.
*
* @param {Object} config - The configuration object containing application data.
*/
export const setAppData = (config) => {
replaceInFile.sync({
files: config.targetDir + '/*',
from: /\{\$appPackage\}/g,
to: config.appPackage,
})

replaceInFile.sync({
files: config.targetDir + '/*',
from: /\{\$appName\}/g,
to: config.appName,
})
}

/**
* Display an error message and exit the program with an error status.
*
* @param {string} msg - The error message to display before exiting the program.
*/
export const exit = (msg) => {
spinnerMsg.fail(msg)
process.exit()
}

/**
* Initialize an empty Git repository in the specified directory and copy a .gitignore file.
*
* @param {string} cwd - The current working directory where the Git repository will be created.
* @param {string} fixturesBase - The base directory for fixtures.
* @returns {Promise} A Promise that resolves when the Git initialization and file copying are completed successfully
*/
export const gitInit = (cwd, fixturesBase) => {
spinnerMsg.start('Initializing empty GIT repository')
let msg
return execa('git', ['init'], { cwd })
.then(({ stdout }) => (msg = stdout))
.then(() => {
return fs.copyFileSync(
path.join(fixturesBase, 'common/git/.gitignore'),
path.join(cwd, '.gitignore')
)
})
.then(() => spinnerMsg.succeed(msg))
.catch((e) => spinnerMsg.fail(`Error occurred while creating git repository\n\n${e}`))
}

/**
* Checks whether the give path is valid
* @param path
* @returns {boolean}
*/
export const isValidPath = (path) => {
try {
// Check if the path exists
fs.accessSync(path, fs.constants.F_OK)
return true
} catch (err) {
// The path does not exist or is not accessible
return false
}
}

export const done = (config) => {
console.log('')
console.log('--------------------------------------------------------------------')
console.log(' Your new boilerplate Lightning 3 App has been created! ⚡️')
console.log('--------------------------------------------------------------------')
console.log('')
console.log('Follow the steps below to get started:')
console.log('')
console.log(` ${green(bold(`cd ${config.appFolder}`))}`)
console.log(` ${green(bold('npm install'))}`)
console.log(` ${green(bold('npm run dev'))}`)
}
25 changes: 0 additions & 25 deletions bin/index.cjs

This file was deleted.

141 changes: 141 additions & 0 deletions bin/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
#!/usr/bin/env node
import path from 'path'
import process from 'process'
import { dirname } from 'path'
import prompts from 'prompts'
import { fileURLToPath } from 'url'
import fs from 'fs'
import { red, bold } from 'kolorist'
import sequence from '../src/helpers/sequence.js'
import validatePackage from 'validate-npm-package-name'
import {
addESlint,
copyLightningFixtures,
setAppData,
setBlitsVersion,
gitInit,
done, spinnerMsg
} from './helpers.js'

const defaultBanner = `
--------------------------------------------------------------------
Welcome to Blits - The App development framework for Lightning 3 ⚡️
--------------------------------------------------------------------
Answer the questions below to set up a new Lightning 3 Blits App
`

console.log(defaultBanner)

const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)

const fixturesBase = path.join(
__dirname,
'../boilerplate')

const questions = [
{
type: 'text',
name: 'appName',
message: 'What is the name of your App?',
format: val => {
// Check if the provided application name is empty
if (!val.trim()) {
spinnerMsg.fail(red(bold("Please provide a name for the App")))
return process.exit(1)
} else {
return val
}
},
initial: 'My Blits App', // Default value for the application name
},
{
type: 'text',
name: 'appPackage',
message: 'What is the package name of your App?',
format: val => {
// Validate the package name using validate-npm-package-name
if (!validatePackage(val).validForNewPackages) {
spinnerMsg.fail(red(bold("Please provide a valid package name")))
return process.exit(1)
} else {
return val
}
},
initial: prev => `${prev.replace(/[\sA-Z]/g, str => str === ' ' ? '-' : str.toLowerCase())}`,
},
{
type: 'text',
name: 'appFolder',
message: 'In what folder (relative to the current location) should the App be generated?',
format: (val, prev) => {
// Regular expression to validate whether the path is Unix/Windows-based
const pathRegex = /([A-Za-z]:)?([\\/]+\w+)+/
// Check if the provided path matches the defined regex
if (pathRegex.test(val)) {
try {
// Check if the path exists
if (fs.statSync(val)) {
// Return the resolved file path using path.join
return `${path.join(val, prev.appPackage)}`
}
} catch (e) {
// Handle case where an error occurred during file system interaction
if (e.code === 'ENOENT') {
spinnerMsg.fail(red(bold("Entered directory path is invalid, please enter a valid directory!")))
process.exit()
}
}
} else if (val === prev.appPackage) {
return path.join(process.cwd(), prev.appPackage)
}
},
initial: (prev, val) => val.appPackage,
},
{
type: 'toggle',
name: 'esLint',
message: 'Do you want to enable eslint?',
initial: 'true',
active: 'Yes',
inactive: 'No',
},
{
type: 'toggle',
name: 'gitInit',
message: 'Do you want to generate an git repository?',
initial: 'true',
active: 'Yes',
inactive: 'No',
},
]


const createApp = () => {
return new Promise(resolve => {
let config
sequence([
async () => {
const onCancel = () => {
process.exit()
}
config = await prompts(questions, { onCancel })
config.fixturesBase = fixturesBase
return config
},
() => {
spinnerMsg.start(`Generating new App "${config.appName}" ...`)
copyLightningFixtures(config).then(targetDir => (config.targetDir = targetDir))
spinnerMsg.succeed()
},
() => setAppData(config),
() => setBlitsVersion(config),
() => config.esLint && addESlint(config),
() => config.gitInit && gitInit(config.targetDir, config.fixturesBase),
() => done(config)
])
})
}

createApp()
10 changes: 0 additions & 10 deletions boilerplate/.vscode/settings.json

This file was deleted.

File renamed without changes.
File renamed without changes.
File renamed without changes.
Loading

0 comments on commit 6780894

Please sign in to comment.