-
Notifications
You must be signed in to change notification settings - Fork 15
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat: Template Init * feat: Template Init * feat: Create App Commander
- Loading branch information
Showing
20 changed files
with
1,400 additions
and
129 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
'@bnb-chain/create-gnfd-app': patch | ||
--- | ||
|
||
feat: 🎉 Create App Commander |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -30,18 +30,19 @@ You can try out some examples directly in your browser through Stackblitz: | |
### Running Examples Locally | ||
|
||
Clone the project and install dependencies: | ||
|
||
```bash | ||
> git clone [email protected]:bnb-chain/greenfield-js-sdk.git | ||
> cd greenfield-js-sdk | ||
> pnpm install | ||
``` | ||
|
||
and build package: | ||
Build package: | ||
```bash | ||
> pnpm run -r build | ||
> pnpm run -F "./packages/**" -r build | ||
``` | ||
|
||
and then copy env template file: | ||
copy env template file: | ||
```bash | ||
> cp .env.simple .env | ||
``` | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
# Create Greemfielf App | ||
|
||
`create-gnfd-app` allows you to create a new Greenfield app within seconds. | ||
|
||
```bash | ||
> npx @bnb-chain/create-gnfd-app | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
import download from 'download-git-repo'; | ||
import path from 'path'; | ||
import { green } from 'picocolors'; | ||
import { PackageManager } from './helpers/get-pkg-manager'; | ||
import { installTemplate, TemplateType } from './helpers/install-template'; | ||
import { isFolderEmpty } from './helpers/is-folder-empty'; | ||
import { isWriteable, makeDir } from './helpers/is-writeable'; | ||
import { failSpinner, startSpinner, succeedSpiner } from './helpers/spinner'; | ||
import { TEMPLATES_MAP } from './templates'; | ||
|
||
export async function createApp({ | ||
appPath, | ||
packageManager, | ||
template, | ||
}: { | ||
appPath: string; | ||
packageManager: PackageManager; | ||
template: TemplateType; | ||
}) { | ||
const root = path.resolve(appPath); | ||
if (!(await isWriteable(path.dirname(root)))) { | ||
/* eslint-disable-next-line no-console */ | ||
console.error( | ||
`The application path is not writable, please check folder permissions and try again. | ||
It is likely you do not have write permissions for this folder. | ||
`, | ||
); | ||
process.exit(1); | ||
} | ||
|
||
const appName = path.basename(root); | ||
await makeDir(root); | ||
if (!isFolderEmpty(root, appName)) { | ||
process.exit(1); | ||
} | ||
|
||
/* eslint-disable-next-line no-console */ | ||
console.log(`Creating a new Greenfield app in ${green(root)}.`); | ||
process.chdir(root); | ||
|
||
startSpinner('downloading template...'); | ||
download(TEMPLATES_MAP[template], '.', { clone: false }, (err) => { | ||
if (err) { | ||
failSpinner(err.message); | ||
return; | ||
} | ||
|
||
succeedSpiner(`download template - ${template} success`); | ||
|
||
return installTemplate({ | ||
appName, | ||
root, | ||
packageManager, | ||
}); | ||
}).then(() => { | ||
// ... | ||
}); | ||
} |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
export type PackageManager = 'npm' | 'pnpm' | 'yarn'; | ||
|
||
export function getPkgManager(): PackageManager { | ||
const userAgent = process.env.npm_config_user_agent || ''; | ||
|
||
if (userAgent.startsWith('yarn')) { | ||
return 'yarn'; | ||
} | ||
|
||
if (userAgent.startsWith('pnpm')) { | ||
return 'pnpm'; | ||
} | ||
|
||
return 'npm'; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export const getTemplateUrl = (template: string) => { | ||
return `https://github.com:rrr523/greenfield-${template}-template#master`; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
import spawn from 'cross-spawn'; | ||
import fs from 'fs'; | ||
import { removeSync } from 'fs-extra'; | ||
import handlebars from 'handlebars'; | ||
import { cyan } from 'picocolors'; | ||
import { PackageManager } from './get-pkg-manager'; | ||
import { failSpinner, startSpinner, succeedSpiner } from './spinner'; | ||
|
||
export const SRC_DIR_NAMES = ['app', 'pages', 'styles']; | ||
|
||
export type TemplateType = 'nextjs' | 'cra'; | ||
export type TemplateMode = 'js' | 'ts'; | ||
|
||
export const installTemplate = async ({ appName, root, packageManager }: InstallTemplateArgs) => { | ||
/** | ||
* Create a package.json for the new project and write it to disk. | ||
*/ | ||
const packageJsonPath = `${root}/package.json`; | ||
const packageJsonContent = fs.readFileSync(packageJsonPath, 'utf-8'); | ||
const packageJsonResult = handlebars.compile(packageJsonContent)({ | ||
name: appName, | ||
}); | ||
fs.writeFileSync(packageJsonPath, packageJsonResult); | ||
|
||
/** | ||
* delete package-lock.json | ||
*/ | ||
removeSync(`${root}/package-lock.json`); | ||
|
||
/** | ||
* install dependencies | ||
*/ | ||
startSpinner(cyan(`Installing dependencies with ${packageManager}...`)); | ||
const child = spawn(packageManager, ['install'], { | ||
// stdio: 'inherit', | ||
// env: { | ||
// ...process.env, | ||
// ADBLOCK: '1', | ||
// NODE_ENV: 'development', | ||
// DISABLE_OPENCOLLECTIVE: '1', | ||
// }, | ||
}); | ||
|
||
child.on('close', (code) => { | ||
if (code !== 0) { | ||
failSpinner('Failed to install dependencies.'); | ||
return; | ||
} | ||
|
||
succeedSpiner('Install dependencies successfully.'); | ||
}); | ||
}; | ||
|
||
export interface InstallTemplateArgs { | ||
appName: string; | ||
root: string; | ||
packageManager: PackageManager; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
/* eslint-disable no-console */ | ||
import { green, blue } from 'picocolors'; | ||
import fs from 'fs'; | ||
import path from 'path'; | ||
|
||
export function isFolderEmpty(root: string, name: string): boolean { | ||
const validFiles = [ | ||
'.DS_Store', | ||
'.git', | ||
'.gitattributes', | ||
'.gitignore', | ||
'.gitlab-ci.yml', | ||
'.hg', | ||
'.hgcheck', | ||
'.hgignore', | ||
'.idea', | ||
'.npmignore', | ||
'.travis.yml', | ||
'LICENSE', | ||
'Thumbs.db', | ||
'docs', | ||
'mkdocs.yml', | ||
'npm-debug.log', | ||
'yarn-debug.log', | ||
'yarn-error.log', | ||
'yarnrc.yml', | ||
'.yarn', | ||
]; | ||
|
||
const conflicts = fs | ||
.readdirSync(root) | ||
.filter((file) => !validFiles.includes(file)) | ||
// Support IntelliJ IDEA-based editors | ||
.filter((file) => !/\.iml$/.test(file)); | ||
|
||
if (conflicts.length > 0) { | ||
console.log(`The directory ${green(name)} contains files that could conflict:`); | ||
console.log(); | ||
for (const file of conflicts) { | ||
try { | ||
const stats = fs.lstatSync(path.join(root, file)); | ||
if (stats.isDirectory()) { | ||
console.log(` ${blue(file)}/`); | ||
} else { | ||
console.log(` ${file}`); | ||
} | ||
} catch { | ||
console.log(` ${file}`); | ||
} | ||
} | ||
console.log(); | ||
console.log('Either try using a new directory name, or remove the files listed above.'); | ||
console.log(); | ||
return false; | ||
} | ||
|
||
return true; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import fs from 'fs'; | ||
|
||
export async function isWriteable(directory: string): Promise<boolean> { | ||
try { | ||
await fs.promises.access(directory, (fs.constants || fs).W_OK); | ||
return true; | ||
} catch (err) { | ||
return false; | ||
} | ||
} | ||
|
||
export function makeDir(root: string, options = { recursive: true }): Promise<string | undefined> { | ||
return fs.promises.mkdir(root, options); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import ora from 'ora'; | ||
import { red } from 'picocolors'; | ||
|
||
const spinner = ora(); | ||
|
||
export const startSpinner = (text?: string) => { | ||
const msg = `${text}...\n`; | ||
spinner.start(msg); | ||
}; | ||
|
||
export const succeedSpiner = (text?: string) => { | ||
spinner.stopAndPersist({ | ||
symbol: '🎉', | ||
text: `${text}\n`, | ||
}); | ||
}; | ||
|
||
export const failSpinner = (text?: string) => { | ||
spinner.fail(red(text)); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
import prompts from '@inquirer/prompts'; | ||
import Commander from 'commander'; | ||
import fs, { readFileSync } from 'fs'; | ||
import path, { resolve } from 'path'; | ||
import { cyan, green } from 'picocolors'; | ||
import validateNpmName from 'validate-npm-package-name'; | ||
import { createApp } from './createApp'; | ||
import { getPkgManager, PackageManager } from './helpers/get-pkg-manager'; | ||
import { TemplateType } from './helpers/install-template'; | ||
import { isFolderEmpty } from './helpers/is-folder-empty'; | ||
|
||
const packageJson = JSON.parse(readFileSync(resolve(__dirname, '../package.json'), 'utf-8')); | ||
let projectPath = ''; | ||
// const projectName = 'xxx'; | ||
|
||
const program = new Commander.Command(packageJson.name); | ||
program | ||
.version(packageJson.version) | ||
.argument('[project-directory]') | ||
.usage(`${green('[project-directory]')} [options]`) | ||
.action((str) => { | ||
projectPath = str; | ||
}) | ||
.allowUnknownOption() | ||
.parse(process.argv); | ||
|
||
async function runInitPrompts(): Promise<void> { | ||
if (!projectPath) { | ||
projectPath = await prompts.input({ | ||
message: 'What is your project named?', | ||
validate: (val) => { | ||
const { validForNewPackages } = validateNpmName(val); | ||
if (!validForNewPackages) { | ||
return 'Invalid NPM name'; | ||
} | ||
return true; | ||
}, | ||
}); | ||
|
||
projectPath = projectPath.trim(); | ||
} | ||
|
||
if (!projectPath) { | ||
/* eslint-disable-next-line no-console */ | ||
console.log( | ||
'\nPlease specify the project directory:\n' + | ||
` ${cyan(program.name())} ${green('<project-directory>')}\n` + | ||
'For example:\n' + | ||
` ${cyan(program.name())} ${green('my-next-app')}\n\n` + | ||
`Run ${cyan(`${program.name()} --help`)} to see all options.`, | ||
); | ||
process.exit(1); | ||
} | ||
|
||
const template: TemplateType = await prompts.select({ | ||
message: 'select a template?', | ||
choices: [ | ||
{ name: 'nextjs', value: 'nextjs' }, | ||
{ name: 'create-react-app', value: 'cra' }, | ||
], | ||
}); | ||
|
||
const packageManager: PackageManager = await prompts.select({ | ||
message: 'select a package manager?', | ||
choices: [ | ||
{ name: 'npm', value: 'npm' }, | ||
{ name: 'yarn', value: 'yarn' }, | ||
{ name: 'pnpm', value: 'pnpm' }, | ||
], | ||
}); | ||
|
||
const resolvedProjectPath = path.resolve(projectPath); | ||
const root = path.resolve(resolvedProjectPath); | ||
const appName = path.basename(root); | ||
const folderExists = fs.existsSync(root); | ||
|
||
if (folderExists && !isFolderEmpty(root, appName)) { | ||
process.exit(1); | ||
} | ||
|
||
try { | ||
await createApp({ | ||
appPath: resolvedProjectPath, | ||
packageManager, | ||
template, | ||
}); | ||
} catch (reason) { | ||
// . | ||
} | ||
} | ||
|
||
runInitPrompts().catch(() => { | ||
// ignore error | ||
}); |
Oops, something went wrong.