@page-ui/wizard
Version:
Landing page components & templates that you can copy & paste
279 lines (246 loc) • 7.77 kB
JavaScript
import ora from 'ora';
import chalk from 'chalk';
import { Command } from 'commander';
import enquirer from 'enquirer';
import fs from 'fs/promises';
import path from 'path';
import { fileURLToPath } from 'url';
import NextJsFiles from './files/next.js';
import ReactFiles from './files/react.js';
import { getFiles } from './files/getFiles.js';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const componentsPath = path.join(__dirname, '../../components');
const AVAILABLE_TEMPLATES = [
{
name: 'nextjs',
},
{
name: 'react',
},
];
const DEFAULT_TEMPLATE = AVAILABLE_TEMPLATES[0];
const copyFile = async (src, dest) => {
return fs.copyFile(src, dest);
};
const copyDirectory = async (src, dest) => {
await fs.mkdir(dest, { recursive: true });
const entries = await fs.readdir(src, { withFileTypes: true });
for (const entry of entries) {
const srcPath = path.join(src, entry.name);
const destPath = path.join(dest, entry.name);
if (entry.isDirectory()) {
await copyDirectory(srcPath, destPath);
} else {
await copyFile(srcPath, destPath);
}
}
};
const ensureDir = async (filePath) => {
const dirname = path.dirname(filePath);
await fs.mkdir(dirname, { recursive: true });
};
const copyFilesAndDirectories = async ({
files = [],
directories = [],
postCopyCommand,
}) => {
try {
// Copy existing files
for (const file of files) {
await ensureDir(file.to);
await copyFile(file.from, path.join(process.cwd(), file.to));
}
// Copy entire directories
for (const directory of directories) {
await copyDirectory(
directory.from,
path.join(process.cwd(), directory.to)
);
}
console.log('\n');
console.log(
chalk.bold(
chalk.green(
`✅ Page UI initialized successfully! Here's what you can do next`
)
)
);
console.log('\n');
console.log(
`- Check docs at ${chalk.bold(
chalk.white('https://pageui.dev/docs/landing-page-components')
)}`
);
console.log(
`- Get started with templates ${chalk.bold(
chalk.white('https://shipixen.com/demo/landing-page-templates')
)}`
);
if (postCopyCommand) {
postCopyCommand();
}
console.log('\n');
} catch (error) {
console.log('\n');
console.error(chalk.red("💥 Couldn't copy files"));
console.error(chalk.red(error));
}
};
const program = new Command();
program.name('page-ui').version('1.0.0');
program
.command('init')
.description('Setup Page UI: adds required files to your project')
.option(
'-t, --template <template>',
`Choose a template from: ${AVAILABLE_TEMPLATES.map(
(template) => template.name
).join(', ')}. Default: ${DEFAULT_TEMPLATE.name}`,
DEFAULT_TEMPLATE.name
)
.option(
'-src, --source <source>',
'Source directory. Defaults to root, but you might be using "src".',
''
)
.action(async (opts) => {
const { template, src } = opts;
if (process.env.DEBUG) {
console.log(JSON.stringify(process.argv, null, 2));
console.log(JSON.stringify({ template, src }, null, 2));
}
let sourceDirectory = src;
if (!src) {
// TODO: Ask for src directory for Next.js
switch (template) {
case 'react':
sourceDirectory = 'src';
break;
default:
sourceDirectory = '';
}
}
let files, directories;
switch (template) {
case 'nextjs':
files = NextJsFiles.FILES;
directories = NextJsFiles.DIRECTORIES;
break;
case 'react':
files = ReactFiles.FILES;
directories = ReactFiles.DIRECTORIES;
break;
default:
files = NextJsFiles.FILES;
directories = NextJsFiles.DIRECTORIES;
}
// TODO: Should Page UI also install npm dependencies?
files = getFiles({
toPathPrefix: sourceDirectory,
filelist: files,
fromPlaceholderReplacement: componentsPath,
toPathIgnore: [
'tailwind.config.js',
'tailwind.config.ts',
'data/config/colors.js',
],
});
directories = getFiles({
toPathPrefix: sourceDirectory,
filelist: directories,
fromPlaceholderReplacement: componentsPath,
});
try {
// Check for existing files and prompt for confirmation.
const existingFilesAndDirectories = [];
// Check which files exist
for (const file of files) {
try {
await fs.access(path.join(process.cwd(), file.to));
existingFilesAndDirectories.push(file.to);
} catch (error) {}
}
// Check which directories exist
for (const directory of directories) {
try {
await fs.access(path.join(process.cwd(), directory.to));
existingFilesAndDirectories.push(directory.to);
} catch (error) {}
}
if (existingFilesAndDirectories.length > 0) {
const response = await enquirer.prompt({
type: 'confirm',
name: 'confirmation',
initial: 'Y',
format: (value) => (value ? 'Y' : 'n'),
message: `The following files/directories will be overwritten:\n${existingFilesAndDirectories
.map((file) => ` - ${chalk.bold(file)}`)
.join(',\n')} \n\nDo you want to proceed?`,
});
if (response.confirmation) {
const spinner = ora('Initializing Page UI').start();
await copyFilesAndDirectories({
files,
directories,
});
spinner.stop();
} else {
const response = await enquirer.prompt({
type: 'confirm',
name: 'skip',
initial: 'Y',
format: (value) => (value ? 'Y' : 'n'),
message: `Would you like to only copy the missing files/directories only? (This will not overwrite any existing files)`,
});
if (response.skip) {
const spinner = ora('Initializing Page UI').start();
await copyFilesAndDirectories({
files: files.filter(
(file) => !existingFilesAndDirectories.includes(file.to)
),
directories: directories.filter(
(directory) =>
!existingFilesAndDirectories.includes(directory.to)
),
postCopyCommand: () => {
console.log(
`\n${chalk.italic(
'Some files where skipped'
)}. See how to set them up manually at ${chalk.bold(
chalk.white(
'https://pageui.shipixen.com/docs/installation/manual-reactjs'
)
)}
`
);
console.log(
`Or download them from the following links:\n${files
.filter((file) =>
existingFilesAndDirectories.includes(file.to)
)
.map((file) => `- ${chalk.bold(file.fallbackPath)}`)
.join('\n')}`
);
},
});
spinner.stop();
} else {
console.log('Cancelling...');
console.log('👋 Until next time');
}
}
} else {
const spinner = ora('Initializing Page UI').start();
await copyFilesAndDirectories({
files,
directories,
});
spinner.stop();
}
} catch (error) {
console.error(chalk.red('😔 No landing pages where made today'));
console.error(chalk.red(error));
}
});
program.parse(process.argv);